package com.smsc.headend.task.engine.service.task.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.SystemClock;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.smsc.headend.common.utils.RedisKeys;
import com.smsc.headend.common.utils.RedisUtils;
import com.smsc.headend.module.asset.entity.ComEntity;
import com.smsc.headend.module.data.dto.RequestTaskDTO;
import com.smsc.headend.module.data.dto.TaskDataDTO;
import com.smsc.headend.module.data.dto.TaskPacketDTO;
import com.smsc.headend.module.report.entity.LogDevicePacket;
import com.smsc.headend.module.task.dto.AtomicTask;
import com.smsc.headend.module.task.dto.Task;
import com.smsc.headend.module.task.entity.DeviceTask;
import com.smsc.headend.module.task.enums.SubTaskStatus;
import com.smsc.headend.module.task.enums.TaskStatus;
import com.smsc.headend.task.engine.mapper.LogDevicePacketMapper;
import com.smsc.headend.task.engine.po.DeviceDetail;
import com.smsc.headend.task.engine.po.PacketTypeDesc;
import com.smsc.headend.task.engine.registry.PacketDecodeRegistry;
import com.smsc.headend.task.engine.service.FeignAssetManagementService;
import com.smsc.headend.task.engine.service.asset.AssetService;
import com.smsc.headend.task.engine.service.protocol.PacketDecodeService;
import com.smsc.headend.task.engine.service.task.DeviceTaskService;
import com.smsc.headend.task.engine.service.task.TaskDataService;
import com.smsc.headend.task.engine.service.task.TaskService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author liangli
 * @date 2020/9/11
 */
@Service
@Slf4j
public class TaskDataServiceImpl implements TaskDataService {
    @Autowired
    TaskService taskService;
    @Autowired
    DeviceTaskService deviceTaskService;
    @Autowired
    RedisUtils redisUtils;
    @Autowired
    FeignAssetManagementService assetManagementService;
    @Autowired
    PacketDecodeRegistry packetDecodeRegistry;
    @Autowired
    AssetService assetService;
    @Autowired
    LogDevicePacketMapper logDevicePacketMapper;


    /**
     * 生成任务的初始时间
     * 入库完成后，更新轮询的message时间
     *
     * @param messageId
     */
    @Override
    public void setMessageIdTime(String messageId) {
        redisUtils.set(RedisKeys.requestMessageId(messageId), SystemClock.now());
        redisUtils.expire(RedisKeys.requestMessageId(messageId), 10 * 60);
    }

    @Override
    public Long getMessageIdTime(String messageId) {
        return Convert.toLong(redisUtils.get(RedisKeys.requestMessageId(messageId)));
    }

    @Override
    public RequestTaskDTO emptyProcessingResult() {
        RequestTaskDTO requestTaskDTO = new RequestTaskDTO();
        requestTaskDTO.setComplete(0);
        return requestTaskDTO;
    }

    @Override
    public List<TaskPacketDTO> getTaskPacket(List<DeviceTask> tasks) {
        List<TaskPacketDTO> result = new ArrayList<>();
        if (CollectionUtils.isEmpty(tasks)) {
            return result;
        }
        return tasks.stream().map(taskService::getTaskPacket)
                .flatMap(Collection::stream).map(this::analysePacket).collect(Collectors.toList());
    }

    @Override
    public List<TaskPacketDTO> getTaskPacketByTaskNo(String taskNo) {
        List<TaskPacketDTO> list = CollUtil.newArrayList();
        List<LogDevicePacket> packets = logDevicePacketMapper.selectList(Wrappers.<LogDevicePacket>lambdaQuery().eq(LogDevicePacket::getTaskNo, taskNo).orderByAsc(LogDevicePacket::getTv));
        for (LogDevicePacket packet : packets) {
            TaskPacketDTO dto = new TaskPacketDTO();
            list.add(dto);
            BeanUtil.copyProperties(packet, dto);
            dto.setHexPacket(packet.getPacketStr());
            analysePacket(dto);
        }
        return list;
    }

    @Override
    public List<DeviceTask> getDeviceTaskTvAndStatus(List<String> taskNos) {
        return taskNos.stream().map(deviceTaskService::getTaskTvAndStatus).collect(Collectors.toList());
    }

    @Override
    public TaskPacketDTO analysePacket(TaskPacketDTO dto) {
        DeviceDetail deviceDetail = assetService.getDeviceDetail(dto.getComId(), dto.getMeterId());
        PacketDecodeService packetDecodeService = packetDecodeRegistry.getService(deviceDetail.getDeviceTypeGroup().getProtocolId().longValue());
        PacketTypeDesc type = packetDecodeService.getPacketType(dto.getHexPacket(), dto.getDirection());
        dto.setType(type.getType());
        dto.setDesc(type.getDesc());
        return dto;
    }


    @Override
    public RequestTaskDTO getTaskProcessingResult(List<DeviceTask> tasks, String messageId) {
        log.info("task size: {}", tasks.size());
        List<RequestTaskDTO.TaskInfo> list = tasks.stream().map(task -> {
            RequestTaskDTO.TaskInfo taskInfo = new RequestTaskDTO.TaskInfo();
            Task taskInRedis = taskService.getTaskByNo(task.getTaskNo());
            List<TaskDataDTO> taskResults = taskService.taskDataPopAll(task.getTaskNo());

            Long deviceId = task.getDeviceId();
            if (deviceId == null) {
                ComEntity comEntity = assetService.getComById(task.getComId());
                deviceId = comEntity.getDeviceId();
            }

            taskInfo.setDeviceId(deviceId);
            Integer taskStatus = deviceTaskService.getTaskStatus(task.getTaskNo());
            taskInfo.setStatus(taskStatus);
            task.setStatus(TaskStatus.forValue(taskStatus).name());
            if (!CollectionUtils.isEmpty(taskResults)) {
                for (TaskDataDTO taskResult : taskResults) {
                    RequestTaskDTO.SubTaskInfo subTaskInfo = new RequestTaskDTO.SubTaskInfo();
                    subTaskInfo.setKey(taskResult.getKey());
                    subTaskInfo.setData(taskResult.getValue());
                    subTaskInfo.setSubStatus(taskResult.getIsSuccess() != null && taskResult.getIsSuccess() ? SubTaskStatus.Success.getCode() : SubTaskStatus.Failed.getCode());
                    taskInfo.getSubTasks().add(subTaskInfo);
                }
            }
            //if task in redis is not same with DeviceTask
            if (taskInRedis != null) {
                //GET PROCESSING ATOMIC TASK
                RequestTaskDTO.SubTaskInfo subTaskInfo = new RequestTaskDTO.SubTaskInfo();
                String atomicTaskStr = taskService.getProcessingAtomicTaskByTaskNo(task.getTaskNo());
                AtomicTask atomicTask = taskInRedis.getSubTaskGroups().stream().map(subTaskGroup ->
                        subTaskGroup.getSubTasks().stream().filter(a -> a.getAtomicTaskNo().equalsIgnoreCase(atomicTaskStr)).findAny().orElse(null)
                ).filter(a -> a != null).findAny().orElse(null);
                subTaskInfo.setSubStatus(SubTaskStatus.Processing.getCode());
                Map<String, Object> map = atomicTask == null || StringUtils.isEmpty(atomicTask.getAtomicTaskParam()) ? null : JSONUtil.toBean(atomicTask.getAtomicTaskParam(), Map.class);
                if (!CollectionUtils.isEmpty(map) && map.containsKey("dataItemId")) {
                    subTaskInfo.setKey(Convert.toStr(map.get("dataItemId")));
                    if (!taskInfo.getSubTasks().stream().filter(s -> s.getKey().equalsIgnoreCase(subTaskInfo.getKey())).findAny().isPresent()) {
                        taskInfo.getSubTasks().add(subTaskInfo);
                    }
                }
            }
            for (RequestTaskDTO.SubTaskInfo subTask : taskInfo.getSubTasks()) {
                log.info(subTask.toString());
            }
            return taskInfo;
        }).collect(Collectors.toList());

        RequestTaskDTO requestTaskDTO = new RequestTaskDTO();
        //存在状态不为complete的task request状态为0
//        log.info(JSONUtil.toJsonPrettyStr(tasks));

        Boolean isNotCompleteTaskExists = tasks.stream().filter(task -> !task.getStatus().equalsIgnoreCase(TaskStatus.Complete.toString())).findAny().isPresent();
        Integer complete = CollectionUtils.isEmpty(tasks) || isNotCompleteTaskExists ? 0 : 1;

        requestTaskDTO.setComplete(complete);
        requestTaskDTO.setTaskList(list);
//        log.info("messageId {} complete {} {}", messageId, complete, requestTaskDTO);
        return requestTaskDTO;
    }
}
